/**************************************************************************
*    Libreria para manejo de LCD en 4 u 8 bits para compilador HiTech     *
***************************************************************************
 
***************************************************************************
* Basado en el mapa de memoria de los display WinStar y los comandos del  *
* HD44780.                                                                *
* Sirve para display 16x1, 16x2 y 16x4                                    *
***************************************************************************
 
***************************************************************************
*                                                                         *
* Antes de incluir este archivo en nuestro programa hay que definir       *
* algunos parametros. Estos son                                           *
*                                                                         *
* _XTAL_FREQ        (es necesario para los delay utilizados internamente) *
* LcdDataPort       (el puerto que se utilizara para los datos)           *
* LcdControlPort    (el puerto que se utilizara para los bits de control) *
* LcdRWBit          (el numero de bit que se utilizara para RW)           *
* LcdRSBit          (el numero de bit que se utilizara para RS)           *
* LcdENBit          (el numero de bit que se utilizara para EN)           *
*                                                                         *
* En el caso de querer usar protocolo de 4 Bits tambien hay que definir   *
*                                                                         *
* LcdD4             (el numero de bit que sera D4 hacia el display)       *
*                                                                         *
*                                                                         *
*   LcdD4 solo puede ser 0 o 4. Si es 0 se utiliza el nibble bajo de      *
* LcdDataPort para enviar los datos al display. Si es 1 se utiliza        *
* el nibble alto.                                                         *
*   Si no esta definido LcdD4, se usa protocolo de 8 bits. En este caso   *
* se usan los 8 bits de LcdDataPort para enviar los datos al display.     *
*                                                                         *
***************************************************************************
 
***************************************************************************
*                                                                         *
*  En esta libreria se encuentran las siguientes funciones                *
*                                                                         *
* InitLcd()                 (inicializa del display)                      *
* ClearLcd()                (borra el display y se posiciona en el inicio)*
* GotoxyLcd(char,char)      (posiciona el cursor en el lugar indicado)    *
* LcdOn()                   (enciende el display)                         *
* LcdOff()                  (apaga el display)                            *
* SetCursorLcd(char,char)   (configura el cursor)                         *
* WriteCharLcd(char)        (envia un caracter al display)                *
* WriteStrLcd(char*)        (envia un String al display)                  *
*                                                                         *
*  El parametro de la funcion anterior puede ser un puntero a un String   *
* o el propio String.                                                     *
*                                                                         *
* ReadBytLcd()              (devuelve el byte leido en la posicion actual)*
* WriteByteLcd(char,char)   (envia un byte al display)                    *
*                                                                         *
*  El segundo parametro de la funcion anterior puede ser 0 o 1 y es el    *
* valor que va a tomar el pin RS.                                         *
*  Por eso, esta funcion puede ser utilizada para enviar una instruccion  * 
* o un caracter al display.                                               *
*                                                                         *
*  Tambien se encuentran definidos los parametros CursorOn, CursorOff,    *
* CursorBlinkOn y CursorBlinkOff que son utilizados por la funcion        *
* SetCursorLcd(char).                                                     *
*  Tambien estan definidos los parametros ShiftDisplay, ShiftCursor,      *
* ShiftRight y ShiftLeft que son utilizados por la funcion LcdShift(char) *    
***************************************************************************
 
***************************************************************************
*                                                                         *
*  Luego de utilizar las funciones, el puerto utilizado para LcdDataPort  *
* retorna a su estado original.                                           *
*  Es decir que el registro TRIS vuelve a configurarse como estaba antes  *
* de utilizar las funciones. Y sobre el puerto vuelve a aparecer el mismo *
* dato que habia antes de utilizar las funciones para el display          *
*                                                                         *
***************************************************************************
 
* Ejemplo de configuracion y utilizacion
 
  #define _XTAL_FREQ 10000000    // Cristal 10MHz
  #define LcdControlPort PORTC   // Uso el puerto A para los bits de control
  #define LcdRWBit 0             // El bit 0 del puerto A para RW
  #define LcdRSBit 1             // El bit 0 del puerto A para RS
  #define LcdENBit 1             // El bit 0 del puerto A para EN
  #define LcdDataPort  PORTB     // Uso el puerto B para los datos
  //#define LcdD4 4              // Solo utilizar para protocolo de 4 Bits
 
#include "SuperLcd.C"            // Incluimos nuestra libreria
 
void main(void)
{
  const char * pantalla = "Mundo";
 
  .....
  .....
  .....
  InitLcd();
  WriteStrLcd ("Hola");          // escribe en la primer linea la palabra Hola
  GotoxyLcd (1,2);
  WriteStrLcd (pantalla);        // escribe en la segunda linea la palabra apuntada por la variable pantalla
  SetCursorLcd (CursorOff | CursorBlinkOn); // apaga el cursor, y lo hace parpadear
  LcdShift (ShiftDisplay | ShiftRight); // dezplaza el display hacia la derecha (texto a la izquierda)
}
 
*/
 
#if defined (LcdControlPort) && defined (LcdRWBit) && defined (LcdRSBit) && defined (LcdENBit) &&\
    defined (LcdDataPort)
  #if defined (LcdD4)			//Define el bit de menos peso del nibble (puede ser 0 o 4)
    #define Protocolo4Bits
    #if (LcdD4 != 0) && (LcdD4 != 4)
      #error Mal definido D4 del LCD
	#endif
  #endif
 
  #define CursorOff        0b1100
  #define CursorOn         0b1110
  #define CursorBlinkOff   0b1100
  #define CursorBlinkOn    0b1101
 
  #define ShiftDisplay     0b11000
  #define ShiftCursor      0b10000
  #define ShiftRight       0b10100
  #define ShiftLeft        0b10000
 
  unsigned char Mascara;
  volatile bit TRISLcdRW @ (unsigned)&LcdControlPort*8 +0x80*8 +LcdRWBit;
  volatile bit TRISLcdRS @ (unsigned)&LcdControlPort*8 +0x80*8 +LcdRSBit;
  volatile bit TRISLcdEN @ (unsigned)&LcdControlPort*8 +0x80*8 +LcdENBit;
  volatile bit LcdRW @ (unsigned)&LcdControlPort*8 +LcdRWBit;
  volatile bit LcdRS @ (unsigned)&LcdControlPort*8 +LcdRSBit;
  volatile bit LcdEN @ (unsigned)&LcdControlPort*8 +LcdENBit;
  volatile char TRISLcdData @ (unsigned)&LcdDataPort + 0x80;
 
 
unsigned char ReadByteLcd2(void)
  {
    unsigned char high, low;
    unsigned char TRISAnterior;
    unsigned char DatoAnterior;
    unsigned char DatoLeido;
 
    DatoAnterior = LcdDataPort;
    TRISAnterior = TRISLcdData;
    #ifdef Protocolo4Bits
      #if (LcdD4 == 0)
        TRISLcdData |= 0x0F;
      #elif (LcdD4 == 4)
        TRISLcdData |= 0xF0;
      #endif
    #else
      TRISLcdData = 0xFF;
    #endif
    LcdRS = 1;
    LcdRW = 1;
    LcdEN = 1;
    __delay_us (1);
    #ifdef Protocolo4Bits
      high = LcdDataPort;
      LcdEN = 0;
      __delay_us (1);
      LcdEN = 1;
      __delay_us (1);
      low = LcdDataPort;
      LcdEN = 0;
      #if (LcdD4 == 0)
        high = high<<4;
        low = low & 0x0F;  
      #elif (LcdD4 == 4)
        high = high & 0xF0;
        low = low>>4;	
      #endif
      DatoLeido = high + low;
    #else
      DatoLeido = LcdDataPort;
      LcdEN = 0;
    #endif
    TRISLcdData = TRISAnterior;
    LcdDataPort = DatoAnterior;
    return DatoLeido;
  }

  unsigned char ReadByteLcd(void)
  {
    unsigned char high, low;
    unsigned char TRISAnterior;
    unsigned char DatoAnterior;
    unsigned char DatoLeido;
 
    DatoAnterior = LcdDataPort;
    TRISAnterior = TRISLcdData;
    #ifdef Protocolo4Bits
      #if (LcdD4 == 0)
        TRISLcdData |= 0x0F;
      #elif (LcdD4 == 4)
        TRISLcdData |= 0xF0;
      #endif
    #else
      TRISLcdData = 0xFF;
    #endif
    LcdRS = 0;
    LcdRW = 1;
    LcdEN = 1;
    __delay_us (1);
    #ifdef Protocolo4Bits
      high = LcdDataPort;
      LcdEN = 0;
      __delay_us (1);
      LcdEN = 1;
      __delay_us (1);
      low = LcdDataPort;
      LcdEN = 0;
      #if (LcdD4 == 0)
        high = high<<4;
        low = low & 0x0F;  
      #elif (LcdD4 == 4)
        high = high & 0xF0;
        low = low>>4;	
      #endif
      DatoLeido = high + low;
    #else
      DatoLeido = LcdDataPort;
      LcdEN = 0;
    #endif
    TRISLcdData = TRISAnterior;
    LcdDataPort = DatoAnterior;
    return DatoLeido;
  }
 
  void WriteNibbleLcd (unsigned char Data)
  {
    #ifdef Protocolo4Bits
      #if (LcdD4 == 0) 
        LcdDataPort = (LcdDataPort & 0xF0) + (Data>>4);
      #elif (LcdD4 == 4) 
        LcdDataPort = (LcdDataPort & 0x0F) + (Data & 0xF0);
      #endif
    #else
      LcdDataPort = Data;
    #endif
    LcdEN = 1;
    __delay_us(2);
    LcdEN = 0;
	return;
  }
 
  void WriteByteLcd(unsigned char Data, unsigned char dir)
  {
    unsigned char TRISAnterior;
    unsigned char DatoAnterior;
 
    DatoAnterior = LcdDataPort;
    TRISAnterior = TRISLcdData;    
    TRISLcdData &= Mascara;
    while ((ReadByteLcd() & 128) == 128);
    __delay_us(1);
    //__delay_us(20);
    LcdRS = (bit)dir;
    LcdRW = 0;
    #ifdef Protocolo4Bits			//Si uso 4 bits separa el byte en 2
      WriteNibbleLcd(Data);
      WriteNibbleLcd(Data<<4);
    #else
      WriteNibbleLcd(Data);			//Si uso 8 bits envio el byte entero
    #endif
    TRISLcdData = TRISAnterior;
    LcdDataPort = DatoAnterior;
	return;
  }
 
  void WriteStrLcd (const char *palabra)
  {
    while (*palabra)
      WriteByteLcd ((*palabra++),1);
	return;
  }
 
  void GotoxyLcd (unsigned char x, unsigned char y)
  {
    if (y == 1) y = 0;
    else
      if (y == 2) y = 0x40;
      else
        if (y == 3) y = 0x10;
        else
          if (y == 4) y = 0x50;
    x--;
    WriteByteLcd (((y+x)|0x80),0);
	return;
  }
 
  #define LcdShift(Estado) WriteByteLcd (Estado,0)
  #define SetCursorLcd(Estado) WriteByteLcd (Estado,0)
  #define WriteCharLcd(letra) WriteByteLcd (letra,1)
  #define LcdOn() WriteByteLcd(0x0C,0)
  #define LcdOff() WriteByteLcd(0x08,0)
  #define ClearLcd() WriteByteLcd(0x01,0)
 
  void InitLcd(void)
  {
    unsigned char TRISAnterior;
    unsigned char DatoAnterior;
    unsigned char Contador;
 
    #ifdef Protocolo4Bits
      #if (LcdD4 == 0)
        Mascara = 0xF0;	
      #elif (LcdD4 == 4)
        Mascara = 0x0F;
      #endif
    #else
      Mascara = 0;
    #endif
    TRISLcdRW = 0;
    TRISLcdRS = 0;
    TRISLcdEN = 0;
    LcdEN = 0;
    TRISAnterior = TRISLcdData;
    DatoAnterior = LcdDataPort;	
    LcdRW = 0;
    LcdRS = 0;
    TRISLcdData &= Mascara;
    __delay_ms (15);
    for (Contador= 3; Contador > 0; Contador--)
    {
      WriteNibbleLcd (0x30);
      __delay_ms (5);
    }
    #ifdef Protocolo4Bits
      WriteNibbleLcd (0x20);			//indica que se escribe en 2 nibbles de 4 bits
      WriteByteLcd (0x28,0);
      // 30 para el st7066
      // 32 para el st7066
    #else
      WriteByteLcd (0x38,0);
    #endif
    LcdOff(); //08
    // 38 para el st7066?
    ClearLcd(); //01
    WriteByteLcd (0x06,0);  //06
    //WriteByteLcd (0x0E,0);
    LcdOn(); //0C
    TRISLcdData = TRISAnterior;
    LcdDataPort = DatoAnterior;
	return;
  }
 
#else
  #error Faltan definir parametros
#endif